# FLEXINVERT project Makefile (authored from roadmap analysis on 2025-10-28).
# Running `make` prints a structured view of essential and optional work items.

.PHONY: all summary todo done progress next prev update help streams stream.summary stream.todo stream.done lint \
        mark mark.start mark.done mark.block mark.wait mark.optional \
        flex.todo flex.next flex.mark.done mark.auto export.md export.json branch

MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
REPO_ROOT := $(abspath $(MAKEFILE_DIR)../..)
PROJECT_ROOT ?= /home/linden/Downloads/FLEXINVERT
INC_PATH ?= $(PROJECT_ROOT) .
MAKEFLAGS += $(foreach dir,$(INC_PATH),-I $(dir))
SUCCESS_SEARCH_DIRS := $(strip $(foreach dir,$(INC_PATH),$(abspath $(dir))))
TASKS_CLI := $(REPO_ROOT)/scripts/tasks_cli.py
CATALOG_ROOT := $(MAKEFILE_DIR)includes
SUCCESS_DEFAULT_DIR := logs/flexinvert/success
FLEX_BRANCH_SUCCESS_DIR_baseline := logs/flexinvert/baseline/success
FLEX_BRANCH_SUCCESS_DIR_optimized := logs/flexinvert/optimized/success
PYTHON ?= python3

BRANCHES := baseline optimized
BRANCH_LABEL_baseline := baseline
BRANCH_LABEL_optimized := optimized
BRANCH_NAME_baseline := Baseline line (Fortran source_original_baseline; Julia mainline)
BRANCH_NAME_optimized := Optimised line (repo_flexinvert v03; Julia parity variants)
BRANCH_DESC_baseline := Use legacy Fortran sources under source_original_baseline and the Julia mainline for production parity.
BRANCH_DESC_optimized := Use the cleaned repo_flexinvert (versions 02/03) plus Julia parity variants for scaling experiments.
BRANCH_FORTRAN_PATH_baseline := $(PROJECT_ROOT)/code/flexinvertplus-master/source_original_baseline
BRANCH_FORTRAN_PATH_optimized := $(PROJECT_ROOT)/code/flexinvertplus-master/repo_flexinvert
BRANCH_FORTRAN_CMD_baseline := cd $(PROJECT_ROOT)/code/flexinvertplus-master/source_original_baseline
BRANCH_FORTRAN_CMD_optimized := git -C $(PROJECT_ROOT)/code/flexinvertplus-master/repo_flexinvert checkout v03
BRANCH_JULIA_PATH_baseline := $(PROJECT_ROOT)/code/julia/FLEXINVERT.jl
BRANCH_JULIA_PATH_optimized := $(PROJECT_ROOT)/code/julia/FLEXINVERT_fortran_rhs_20251024
BRANCH_JULIA_CMD_baseline := git -C $(PROJECT_ROOT)/code/julia/FLEXINVERT.jl checkout main
BRANCH_JULIA_CMD_optimized := git -C $(PROJECT_ROOT)/code/julia/FLEXINVERT_fortran_rhs_20251024 checkout main
BRANCH_NOTES_baseline := See REORG_SUMMARY.md and branch_index.json for baseline lineage (versions 00/01).
BRANCH_NOTES_optimized := Align Fortran repo_flexinvert (v02/v03) with Julia parity helpers (FLEXINVERT_fortran_rhs_20251024).

BRANCH_SELECTION_RAW := $(strip $(BRANCH))
BRANCH_SELECTION := $(if $(BRANCH_SELECTION_RAW),$(BRANCH_SELECTION_RAW),baseline)
BRANCH_SELECTION_LIST := $(if $(filter all,$(BRANCH_SELECTION)),$(BRANCHES),$(BRANCH_SELECTION))
INVALID_BRANCHES := $(filter-out $(BRANCHES),$(BRANCH_SELECTION_LIST))
ifneq ($(INVALID_BRANCHES),)
$(error Unknown BRANCH value(s): $(INVALID_BRANCHES). Valid options: all $(BRANCHES))
endif

NEXT_ARGS := $(filter-out next,$(MAKECMDGOALS))
NEXT_INDEX_RAW := $(firstword $(NEXT_ARGS))
NEXT_INDEX := $(strip $(shell if [ -n "$(NEXT_INDEX_RAW)" ] && printf '%s' "$(NEXT_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo "$(NEXT_INDEX_RAW)"; else echo 1; fi))
NEXT_ARG_IS_NUM := $(strip $(shell if [ -n "$(NEXT_INDEX_RAW)" ] && printf '%s' "$(NEXT_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo 1; else echo 0; fi))

PREV_ARGS := $(filter-out prev,$(MAKECMDGOALS))
PREV_INDEX_RAW := $(firstword $(PREV_ARGS))
PREV_INDEX := $(strip $(shell if [ -n "$(PREV_INDEX_RAW)" ] && printf '%s' "$(PREV_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo "$(PREV_INDEX_RAW)"; else echo 1; fi))
PREV_ARG_IS_NUM := $(strip $(shell if [ -n "$(PREV_INDEX_RAW)" ] && printf '%s' "$(PREV_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo 1; else echo 0; fi))

ifneq (,$(filter next,$(MAKECMDGOALS)))
ifeq ($(NEXT_ARG_IS_NUM),1)
.PHONY: $(NEXT_INDEX_RAW)
$(NEXT_INDEX_RAW):
	@:
endif
endif

ifneq (,$(filter prev,$(MAKECMDGOALS)))
ifeq ($(PREV_ARG_IS_NUM),1)
.PHONY: $(PREV_INDEX_RAW)
$(PREV_INDEX_RAW):
	@:
endif
endif

# stream.next numeric index support (e.g., `make stream.next 2`)
STREAM_NEXT_ARGS := $(filter-out stream.next,$(MAKECMDGOALS))
STREAM_NEXT_INDEX_RAW := $(firstword $(STREAM_NEXT_ARGS))
STREAM_NEXT_INDEX := $(strip $(shell if [ -n "$(STREAM_NEXT_INDEX_RAW)" ] && printf '%s' "$(STREAM_NEXT_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo "$(STREAM_NEXT_INDEX_RAW)"; else echo 1; fi))
STREAM_NEXT_ARG_IS_NUM := $(strip $(shell if [ -n "$(STREAM_NEXT_INDEX_RAW)" ] && printf '%s' "$(STREAM_NEXT_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo 1; else echo 0; fi))

ifneq (,$(filter stream.next,$(MAKECMDGOALS)))
ifeq ($(STREAM_NEXT_ARG_IS_NUM),1)
.PHONY: $(STREAM_NEXT_INDEX_RAW)
$(STREAM_NEXT_INDEX_RAW):
	@:
endif
endif

# stream.prev numeric index support (e.g., `make stream.prev 2`)
STREAM_PREV_ARGS := $(filter-out stream.prev,$(MAKECMDGOALS))
STREAM_PREV_INDEX_RAW := $(firstword $(STREAM_PREV_ARGS))
STREAM_PREV_INDEX := $(strip $(shell if [ -n "$(STREAM_PREV_INDEX_RAW)" ] && printf '%s' "$(STREAM_PREV_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo "$(STREAM_PREV_INDEX_RAW)"; else echo 1; fi))
STREAM_PREV_ARG_IS_NUM := $(strip $(shell if [ -n "$(STREAM_PREV_INDEX_RAW)" ] && printf '%s' "$(STREAM_PREV_INDEX_RAW)" | grep -Eq '^[0-9]+$$'; then echo 1; else echo 0; fi))

ifneq (,$(filter stream.prev,$(MAKECMDGOALS)))
ifeq ($(STREAM_PREV_ARG_IS_NUM),1)
.PHONY: $(STREAM_PREV_INDEX_RAW)
$(STREAM_PREV_INDEX_RAW):
	@:
endif
endif

all: summary

summary: ## List all tasks with status indicators
	$(info FLEXINVERT Task Rollup (2025-10-28))
	$(foreach stream,$(STREAM_ORDER),$(call PRINT_STREAM,$(stream)))
	@:

todo: ## Show only pending, in-progress, or optional tasks
	$(info FLEXINVERT Pending Tasks)
	$(foreach stream,$(STREAM_ORDER),$(call PRINT_STREAM_FILTER,$(stream),todo wip waiting blocked optional))
	@:

done: ## Show completed tasks
	$(info FLEXINVERT Completed Tasks)
	$(foreach stream,$(STREAM_ORDER),$(call PRINT_STREAM_FILTER,$(stream),done))
	@:

progress: ## Show overall and per-stream completion metrics
	$(info FLEXINVERT Progress Metrics)
	$(info Overall: done=$(OVERALL_DONE) / total=$(OVERALL_TOTAL) ($(call percent,$(OVERALL_DONE),$(OVERALL_TOTAL))) todo=$(OVERALL_TODO) wip=$(OVERALL_WIP) waiting=$(OVERALL_WAITING) blocked=$(OVERALL_BLOCKED) optional=$(OVERALL_OPTIONAL))
	$(foreach stream,$(STREAM_ORDER),$(call STREAM_PROGRESS_LINE,$(stream)))
	@:

next: ## Show the next recommended task and implementation prompt
	$(if $(strip $(next_task)),$(info Next task to execute (position $(NEXT_INDEX) of $(pending_total)):)$(info $(call format_task,$(next_task)))$(call PRINT_NEXT_PROMPT,$(next_task)),$(if $(strip $(pending_queue)),$(info Requested index $(NEXT_INDEX) exceeds pending task count $(pending_total).)$(info Use make \"next $(pending_total)\" to view the last pending task.),$(info All FLEXINVERT tasks are complete.)))
	@:

prev: ## Show the most recently completed task (pass index to step further back)
	$(if $(strip $(prev_task)),$(info Most recently completed task (position $(PREV_INDEX) of $(completed_total)):)$(info $(call format_task,$(prev_task)))$(call PRINT_PREV_PROMPT,$(prev_task)),$(if $(completed_total),$(info Requested index $(PREV_INDEX) exceeds completed task count $(completed_total).)$(info Use make \"prev $(completed_total)\" to view the oldest completed task.),$(info No completed FLEXINVERT tasks found yet.)))
	@:

update: ## Mark successive tasks as done when success indicators exist
	@$(PYTHON) $(TASKS_CLI) autodone --root $(CATALOG_ROOT) --repo $(PROJECT_ROOT) --extra-success-roots "$(REPO_ROOT)" --success-dir $(SUCCESS_DEFAULT_DIR)

streams: ## List stream labels
	$(info FLEXINVERT Streams)
	$(foreach stream,$(STREAM_ORDER),$(info $(stream): $(call stream_label,$(stream))))
	@:

help: ## Print available targets
	$(info Available targets:)
	$(info   make            - alias for make summary)
	$(info   make summary    - list all tasks with status markers)
	$(info   make todo       - show pending or optional tasks)
	$(info   make done       - show completed tasks)
	$(info   make progress   - show completion metrics and per-stream summary)
	$(info   make next       - show the next actionable task with guidance)
	$(info   make prev       - show the most recent completed task (supports numeric index))
	$(info   make update     - auto-mark tasks done when success indicators exist)
	$(info   BRANCH=baseline make todo  - default view (only baseline tasks))
	$(info   BRANCH=all make summary    - show both baseline and optimized tasks)
	$(info   make mark TASK=<ID> STATUS=<state> [OWNER=... DUE=... PRIO=...])
	$(info   make mark.start TASK=<ID> - set STATUS=wip)
	$(info   make mark.done  TASK=<ID> - set STATUS=done)
	$(info   make mark.block TASK=<ID> - set STATUS=blocked)
	$(info   make mark.wait  TASK=<ID> - set STATUS=waiting)
	$(info   make mark.optional TASK=<ID> - set STATUS=optional)
	$(info   make streams    - show stream key)
	$(info   make stream.todo STREAMS=\"...\" - list tasks in named streams)
	$(info   make stream.todo TASK=<ID> - expand a stream bundle into sub-tasks)
	$(info   make stream.next [N] TASK=<ID> - show the Nth pending sub-task (default 1) inside a stream bundle)
	$(info   make stream.prev [N] TASK=<ID> - show the Nth most recent completed sub-task (default 1) inside a stream bundle)
	$(info   make flex.todo  - alias: list pending tasks)
	$(info   make flex.next  - alias: show next task)
	$(info   make flex.mark.done TASK=<ID> [EVID=...] - mark a task done)
	$(info   make mark.auto TASK=<ID> STATUS=<state> - auto-attach evidence from git diff)
	$(info   make branch TASK=<ID> - show branch-specific prep commands)
	$(info   make export.md  - write markdown summary of tasks)
	$(info   make export.json- write JSON summary of tasks)
	@:

stream.summary: ## Show selected streams in STREAMS variable
	$(if $(strip $(STREAMS)),$(info FLEXINVERT Stream Summary ($(STREAMS)))$(foreach stream,$(STREAMS),$(call PRINT_STREAM,$(stream))),$(error STREAMS not set for stream.summary))
	@:

stream.todo: ## Show pending tasks for selected streams or drill into a stream bundle
	$(if $(strip $(STREAMS)),$(info FLEXINVERT Stream Pending ($(STREAMS)))$(foreach stream,$(STREAMS),$(call PRINT_STREAM_FILTER,$(stream),todo wip waiting blocked optional)),$(if $(strip $(STREAM_TARGET_TASK)),$(if $(call is_aggregate,$(STREAM_TARGET_TASK)),$(info Stream $(STREAM_TARGET_TASK) bundle overview)$(info   Total sub-tasks: $(call aggregate_total,$(STREAM_TARGET_TASK)))$(info   Completed: $(call aggregate_done_count,$(STREAM_TARGET_TASK)))$(info   Pending: $(call aggregate_pending_count,$(STREAM_TARGET_TASK)))$(foreach sub,$(call aggregate_deps,$(STREAM_TARGET_TASK)),$(info $(call format_task,$(sub)))),$(error Task $(STREAM_TARGET_TASK) is not a registered stream bundle)),$(error STREAMS not set for stream.todo (and no stream bundle inferred from next task))))
	@:

stream.done: ## Show completed tasks for selected streams (STREAMS="PARITY SOLVER")
	$(if $(strip $(STREAMS)),$(info FLEXINVERT Stream Completed ($(STREAMS)))$(foreach stream,$(STREAMS),$(call PRINT_STREAM_FILTER,$(stream),done)),$(error STREAMS not set for stream.done))
	@:


stream.next: ## Show the next pending sub-task within a stream bundle (defaults to next stream in queue). Supports `stream.next N`.
	$(if $(strip $(STREAM_TARGET_TASK)),,$(error TASK= required for stream.next (no stream bundle inferred from next task)))
	$(if $(call is_aggregate,$(STREAM_TARGET_TASK)),,$(error Task $(STREAM_TARGET_TASK) is not a registered stream bundle))
	$(eval _stream_pending := $(call aggregate_pending,$(STREAM_TARGET_TASK)))
	$(eval _stream_pending_total := $(words $(_stream_pending)))
	$(eval _stream_next_task := $(word $(STREAM_NEXT_INDEX),$(_stream_pending)))
	$(if $(_stream_next_task),$(info Next sub-task within $(STREAM_TARGET_TASK) (position $(STREAM_NEXT_INDEX) of $(_stream_pending_total)):)$(call PRINT_PROMPT_CORE,$(_stream_next_task))$(info   - Parent stream progress: done=$(call aggregate_done_count,$(STREAM_TARGET_TASK)) / total=$(call aggregate_total,$(STREAM_TARGET_TASK)))$(info ),$(if $(_stream_pending_total),$(info Requested index $(STREAM_NEXT_INDEX) exceeds pending count $(_stream_pending_total).)$(info Use `make stream.next $(_stream_pending_total)` to view the last pending sub-task of $(STREAM_TARGET_TASK).),$(info All sub-tasks for $(STREAM_TARGET_TASK) are complete.)))
	@:


stream.prev: ## Show the most recently completed sub-task within a stream bundle (defaults to next stream in queue). Supports `stream.prev N`.
	$(if $(strip $(STREAM_TARGET_TASK)),,$(error TASK= required for stream.prev (no stream bundle inferred from next task)))
	$(if $(call is_aggregate,$(STREAM_TARGET_TASK)),,$(error Task $(STREAM_TARGET_TASK) is not a registered stream bundle))
	$(eval _stream_done := $(call aggregate_done,$(STREAM_TARGET_TASK)))
	$(eval _stream_done_total := $(words $(_stream_done)))
	$(eval _stream_done_rev := $(call reverse_list,$(_stream_done)))
	$(eval _stream_prev_task := $(word $(STREAM_PREV_INDEX),$(_stream_done_rev)))
	$(if $(_stream_prev_task),$(info Most recently completed sub-task within $(STREAM_TARGET_TASK) (position $(STREAM_PREV_INDEX) of $(_stream_done_total)):)$(call PRINT_PREV_PROMPT,$(_stream_prev_task))$(info   - Parent stream progress: done=$(call aggregate_done_count,$(STREAM_TARGET_TASK)) / total=$(call aggregate_total,$(STREAM_TARGET_TASK)))$(info ),$(if $(_stream_done_total),$(info Requested index $(STREAM_PREV_INDEX) exceeds completed count $(_stream_done_total).)$(info Use `make stream.prev $(_stream_done_total)` to view the oldest completed sub-task of $(STREAM_TARGET_TASK).),$(info No completed sub-tasks for $(STREAM_TARGET_TASK) yet.)))
	@:

lint: ## Validate dependency ordering (warn if prerequisites incomplete)
	$(info FLEXINVERT Dependency Check)
	$(foreach task,$(DEP_TARGETS),$(call CHECK_TASK_DEPS,$(task)))
	@:

mark: ## Update task STATUS/metadata (TASK=<ID> STATUS=todo|wip|done|blocked|waiting|optional)
	@if [ -z "$(TASK)" ]; then \
	  echo "TASK= required (e.g., FLEX_P52)" 1>&2; \
	  exit 2; \
	fi
	@if [ -z "$(STATUS)" ]; then \
	  echo "STATUS= required (todo|wip|done|blocked|waiting|optional)" 1>&2; \
	  exit 2; \
	fi
	@$(PYTHON) $(TASKS_CLI) set --root $(CATALOG_ROOT) --task $(TASK) --status $(STATUS)$(if $(OWNER), --owner "$(OWNER)",)$(if $(DUE), --due "$(DUE)",)$(if $(PRIO), --prio "$(PRIO)",)$(if $(TAGS), --tags "$(TAGS)",)$(if $(CRIT), --crit "$(CRIT)",)$(if $(EVID), --evid "$(EVID)",)

flex.todo: ## Convenience alias for `make todo`
	@$(MAKE) -s todo

flex.next: ## Convenience alias for `make next`
	@$(MAKE) -s next

flex.mark.done: ## Convenience: mark a task done (TASK=<ID>)
	@$(MAKE) -s mark TASK=$(TASK) STATUS=done EVID="$(EVID)"

branch: ## Display branch checkout and workspace guidance for TASK=<ID>
	@if [ -z "$(TASK)" ]; then \
	  echo "TASK= required (e.g., FLEX_B2_BASE)" 1>&2; \
	  exit 2; \
	fi; \
	branch='$(call branch_of,$(TASK))'; \
	if [ -z "$$branch" ]; then \
	  echo "Branch metadata not found for $(TASK)" 1>&2; \
	  exit 2; \
	fi; \
	name='$(call branch_name,$(TASK))'; \
	desc='$(call branch_desc,$(TASK))'; \
	fortran_path='$(call branch_fortran_path,$(TASK))'; \
	fortran_cmd='$(call branch_fortran_cmd,$(TASK))'; \
	julia_path='$(call branch_julia_path,$(TASK))'; \
	julia_cmd='$(call branch_julia_cmd,$(TASK))'; \
	notes='$(call branch_notes,$(TASK))'; \
	printf "Task %s\n" "$(TASK)"; \
	printf "  Branch: %s — %s\n" "$$branch" "$$name"; \
	if [ -n "$$desc" ]; then printf "  Notes: %s\n" "$$desc"; fi; \
	if [ -n "$$fortran_path" ]; then printf "  Fortran workspace: %s\n" "$$fortran_path"; fi; \
	if [ -n "$$fortran_cmd" ]; then printf "    Fortran prep: %s\n" "$$fortran_cmd"; fi; \
	if [ -n "$$julia_path" ]; then printf "  Julia workspace: %s\n" "$$julia_path"; fi; \
	if [ -n "$$julia_cmd" ]; then printf "    Julia prep: %s\n" "$$julia_cmd"; fi; \
	if [ -n "$$notes" ]; then printf "  Additional context: %s\n" "$$notes"; fi

# Auto-evidence: attach changed files when EVID unspecified
mark.auto: ## Update task with STATUS and auto-attach git-changed files as evidence
	@if [ -z "$(TASK)" ]; then echo "TASK= required" 1>&2; exit 2; fi
	@if [ -z "$(STATUS)" ]; then echo "STATUS= required" 1>&2; exit 2; fi
	@ev=$${EVID:-$(shell git -C $(REPO_ROOT) diff --name-only --relative HEAD | tr '\n' ' ')}; \
	 if [ -z "$$ev" ]; then ev=$$(git -C $(REPO_ROOT) ls-files -m -o --exclude-standard | tr '\n' ' '); fi; \
	 $(PYTHON) $(TASKS_CLI) set --root $(CATALOG_ROOT) --task $(TASK) --status $(STATUS) --evid "$$ev"

# Lightweight exports using tasks_cli order + grep; full exporter can be added later
export.md: ## Write a simple Markdown rollup of tasks to stdout (redirect to file if needed)
	@echo "# FLEXINVERT Tasks"; \
	 echo; \
	 for s in $$(echo $(STREAM_ORDER)); do \
	   echo "## $$(echo $$s | sed 's/_/ /g')"; \
	   awk '/\$\(call REGISTER_TASK/ {print}' $(CATALOG_ROOT)/*.mk | \
	   grep ",$${s}," | sed -E 's/.*REGISTER_TASK,([^,]+),([^,]+),([^,]+),([^,]+),(.*)/- [\2] (\1) \5/' || true; \
	   echo; \
	 done

export.json: ## Write a simple JSON list of tasks to stdout (redirect to file if needed)
	@python3 - "$@" << 'PY'
	import json, re, sys, pathlib
	root = pathlib.Path('$(CATALOG_ROOT)')
	tasks = []
	pat = re.compile(r"\$\(call REGISTER_TASK,([^,]+),([^,]+),([^,]+),([^,]+),(.+)\)")
	for p in sorted(root.glob('*.mk')):
	    for line in p.read_text().splitlines():
	        m=pat.match(line.strip())
	        if m:
	            tasks.append({"id":m.group(1),"status":m.group(2),"class":m.group(3),"stream":m.group(4),"title":m.group(5),"file":str(p)})
	print(json.dumps(tasks, indent=2))
	PY

mark.start mark.done mark.block mark.wait mark.optional: ## Convenience wrappers for common statuses
	@if [ -z "$(TASK)" ]; then \
	  echo "TASK= required (e.g., FLEX_P52)" 1>&2; \
	  exit 2; \
	fi
	@status=$(if $(filter mark.start,$@),wip,$(if $(filter mark.done,$@),done,$(if $(filter mark.block,$@),blocked,$(if $(filter mark.wait,$@),waiting,optional)))); \
	$(MAKE) --no-print-directory -f $(firstword $(MAKEFILE_LIST)) mark TASK="$(TASK)" STATUS="$$status" OWNER="$(OWNER)" DUE="$(DUE)" PRIO="$(PRIO)" TAGS="$(TAGS)" CRIT="$(CRIT)" EVID="$(EVID)"

# --- Task registration framework -------------------------------------------------

define REGISTER_TASK
$(eval TASK_IDS += $(1))
$(eval STATUS_$(1) := $(2))
$(eval CLASS_$(1) := $(3))
$(eval STREAM_$(1) := $(4))
$(eval TITLE_$(1) := $(5))
$(eval REF_$(1) := $(6))
endef

define REGISTER_META
$(eval OWNER_$(1) := $(2))
$(eval DUE_$(1) := $(3))
$(eval PRIO_$(1) := $(4))
$(eval TAGS_$(1) := $(5))
$(eval CRIT_$(1) := $(6))
$(eval EVID_$(1) := $(7))
endef

define REGISTER_SUCCESS
$(eval SUCCESS_$(1) := $(2))
endef

AGGREGATE_TASKS :=
define REGISTER_AGGREGATE
$(eval AGGREGATE_TASKS += $(1))
$(eval AGGREGATE_DEPS_$(1) := $(strip $(2)))
endef

aggregate_deps = $(strip $(AGGREGATE_DEPS_$(1)))
aggregate_pending = $(strip $(foreach dep,$(call aggregate_deps,$(1)),$(if $(filter done,$(call effective_status,$(dep))),,$(dep))))
is_aggregate = $(if $(filter $(1),$(AGGREGATE_TASKS)),true,)
aggregate_done = $(strip $(foreach dep,$(call aggregate_deps,$(1)),$(if $(filter done,$(call effective_status,$(dep))),$(dep),)))
aggregate_total = $(words $(call aggregate_deps,$(1)))
aggregate_pending_count = $(words $(call aggregate_pending,$(1)))
aggregate_done_count = $(words $(call aggregate_done,$(1)))

success_relative = $(if $(SUCCESS_$(1)),$(SUCCESS_$(1)),$(SUCCESS_DEFAULT_DIR)/$(1).md)
success_absolute = $(if $(filter /%,$(call success_relative,$(1))),$(call success_relative,$(1)))
success_candidates = $(if $(call success_absolute,$(1)),$(call success_absolute,$(1)),$(foreach dir,$(SUCCESS_SEARCH_DIRS),$(dir)/$(call success_relative,$(1))))
success_resolved = $(firstword $(foreach path,$(call success_candidates,$(1)),$(strip $(wildcard $(path)))))
success_exists = $(if $(strip $(call success_resolved,$(1))),true,)
success_display = $(if $(strip $(call success_resolved,$(1))),$(call success_resolved,$(1)),$(firstword $(call success_candidates,$(1))))
effective_status = $(if $(call success_exists,$(1)),done,$(STATUS_$(1)))

branch_of = $(strip $(BRANCH_OF_$(1)))
branch_label = $(if $(call branch_of,$(1)),$(BRANCH_LABEL_$(call branch_of,$(1))),)
branch_name = $(if $(call branch_of,$(1)),$(BRANCH_NAME_$(call branch_of,$(1))),)
branch_desc = $(if $(call branch_of,$(1)),$(BRANCH_DESC_$(call branch_of,$(1))),)
branch_fortran_path = $(if $(call branch_of,$(1)),$(BRANCH_FORTRAN_PATH_$(call branch_of,$(1))),)
branch_fortran_cmd = $(if $(call branch_of,$(1)),$(BRANCH_FORTRAN_CMD_$(call branch_of,$(1))),)
branch_julia_path = $(if $(call branch_of,$(1)),$(BRANCH_JULIA_PATH_$(call branch_of,$(1))),)
branch_julia_cmd = $(if $(call branch_of,$(1)),$(BRANCH_JULIA_CMD_$(call branch_of,$(1))),)
branch_notes = $(if $(call branch_of,$(1)),$(BRANCH_NOTES_$(call branch_of,$(1))),)

STATUS_EMOJI_done := [x]
STATUS_EMOJI_wip := [~]
STATUS_EMOJI_blocked := [!]
STATUS_EMOJI_waiting := [?]
STATUS_EMOJI_optional := ( )

status_symbol = $(or $(STATUS_EMOJI_$(1)),[ ])
class_badge = $(if $(filter optional,$(1)),[O],[E])
stream_label = $(or $(STREAM_LABEL_$(1)),$(1))

define branch_visible
$(if $(call branch_of,$(1)),$(if $(filter $(call branch_of,$(1)),$(BRANCH_SELECTION_LIST)),$(1),),$(1))
endef

tasks_for_stream = $(strip $(foreach t,$(TASK_IDS),$(if $(call branch_visible,$(t)),$(if $(filter $(1),$(STREAM_$(t))),$(t),),)))
tasks_for_stream_status = $(strip $(foreach t,$(call tasks_for_stream,$(1)),$(if $(filter $(call effective_status,$(t)),$(2)),$(t),)))

define reverse_list
$(if $1,$(call reverse_list,$(wordlist 2,$(words $1),$1)) $(firstword $1))
endef

define format_task
$(call status_symbol,$(call effective_status,$(1))) $(call class_badge,$(CLASS_$(1))) $(1)$(if $(call branch_label,$(1)), {$(call branch_label,$(1))},)$(if $(call is_aggregate,$(1)), [stream],) - $(TITLE_$(1)) [$(REF_$(1))]
endef

define tasks_in_statuses
$(strip $(foreach t,$(TASK_IDS),$(if $(call branch_visible,$(t)),$(if $(filter $(call effective_status,$(t)),$(1)),$(t),),)))
endef

status_count = $(words $(call tasks_in_statuses,$(1)))
stream_total = $(words $(call tasks_for_stream,$(1)))
stream_status_count = $(words $(call tasks_for_stream_status,$(1),$(2)))
stream_done = $(call stream_status_count,$(1),done)

define percent
$(shell awk 'BEGIN { total=$(2); done=$(1); if (total==0) { printf "0.0%%"; } else { printf "%.1f%%", (done*100.0)/total; } }')
endef

OVERALL_TOTAL = $(words $(TASK_IDS))
OVERALL_DONE = $(call status_count,done)
OVERALL_TODO = $(call status_count,todo)
OVERALL_WIP = $(call status_count,wip)
OVERALL_WAITING = $(call status_count,waiting)
OVERALL_BLOCKED = $(call status_count,blocked)
OVERALL_OPTIONAL = $(call status_count,optional)
OVERALL_ACTIVE = $(call status_count,$(PENDING_STATUSES))

define STREAM_PROGRESS_LINE
$(info $(call stream_label,$(1)): done=$(call stream_done,$(1)) / total=$(call stream_total,$(1)) ($(call percent,$(call stream_done,$(1)),$(call stream_total,$(1)))) todo=$(call stream_status_count,$(1),todo) wip=$(call stream_status_count,$(1),wip) waiting=$(call stream_status_count,$(1),waiting) blocked=$(call stream_status_count,$(1),blocked) optional=$(call stream_status_count,$(1),optional))
endef

PENDING_STATUSES := todo wip waiting blocked optional
ORDERED_PENDING_ALL := $(strip $(shell $(PYTHON) $(TASKS_CLI) order --root $(CATALOG_ROOT)))
ORDERED_PENDING := $(strip $(foreach t,$(ORDERED_PENDING_ALL),$(if $(call branch_visible,$(t)),$(if $(filter $(call effective_status,$(t)),$(PENDING_STATUSES)),$(if $(call success_exists,$(t)),,$(t))),)))
fallback_pending_essential = $(strip $(foreach t,$(TASK_IDS),$(if $(call branch_visible,$(t)),$(if $(filter essential,$(CLASS_$(t))),$(if $(filter $(call effective_status,$(t)),$(PENDING_STATUSES)),$(if $(call success_exists,$(t)),,$(t))),))))
fallback_pending_optional = $(strip $(foreach t,$(TASK_IDS),$(if $(call branch_visible,$(t)),$(if $(filter optional,$(CLASS_$(t))),$(if $(filter $(call effective_status,$(t)),$(PENDING_STATUSES)),$(if $(call success_exists,$(t)),,$(t))),))))
fallback_pending = $(strip $(fallback_pending_essential) $(fallback_pending_optional))
pending_queue = $(strip $(if $(ORDERED_PENDING),$(ORDERED_PENDING),$(fallback_pending)))
pending_total = $(words $(pending_queue))
next_task = $(word $(NEXT_INDEX),$(pending_queue))
task_dependencies = $(if $(strip $(DEPS_$(1))),$(DEPS_$(1)),none)
deps_pending = $(strip $(foreach dep,$(DEPS_$(1)),$(if $(filter done,$(call effective_status,$(dep))),,$(dep))))
STREAM_DEFAULT_TASK = $(if $(call is_aggregate,$(next_task)),$(next_task))
STREAM_TARGET_TASK = $(if $(strip $(TASK)),$(TASK),$(STREAM_DEFAULT_TASK))

COMPLETED_STATUSES := done
ORDERED_DONE_ALL := $(strip $(shell $(PYTHON) $(TASKS_CLI) order --root $(CATALOG_ROOT) --statuses $(COMPLETED_STATUSES)))
ORDERED_DONE = $(strip $(foreach t,$(ORDERED_DONE_ALL),$(if $(call branch_visible,$(t)),$(if $(filter done,$(call effective_status,$(t))),$(t)),)))
fallback_done = $(strip $(foreach t,$(TASK_IDS),$(if $(call branch_visible,$(t)),$(if $(filter done,$(call effective_status,$(t))),$(t),),)))
completed_queue = $(strip $(if $(ORDERED_DONE),$(ORDERED_DONE),$(fallback_done)))
completed_queue_rev = $(strip $(call reverse_list,$(completed_queue)))
completed_total = $(words $(completed_queue_rev))
prev_task = $(word $(PREV_INDEX),$(completed_queue_rev))

define PRINT_PROMPT_CORE
$(info Implementation prompt:)
$(info   - Objective: $(TITLE_$(1)))
$(info   - Stream: $(call stream_label,$(STREAM_$(1))))
$(if $(call branch_name,$(1)),$(info   - Branch: $(call branch_name,$(1)) ($(call branch_label,$(1))))$(if $(call branch_desc,$(1)),$(info     - Notes: $(call branch_desc,$(1)))),)
$(if $(call branch_fortran_path,$(1)),$(info   - Fortran workspace: $(call branch_fortran_path,$(1)))$(if $(call branch_fortran_cmd,$(1)),$(info     - Fortran prep: $(call branch_fortran_cmd,$(1)))),)
$(if $(call branch_julia_path,$(1)),$(info   - Julia workspace: $(call branch_julia_path,$(1)))$(if $(call branch_julia_cmd,$(1)),$(info     - Julia prep: $(call branch_julia_cmd,$(1)))),)
$(if $(call branch_notes,$(1)),$(info   - Branch notes: $(call branch_notes,$(1))),)
$(info   - Current status: $(call effective_status,$(1)) ($(CLASS_$(1))))
$(info   - Dependencies: $(call task_dependencies,$(1)))
$(info   - Reference doc: $(REF_$(1)))
$(info   - Success indicator: $(call success_display,$(1)))
$(info     Search order: $(SUCCESS_SEARCH_DIRS))
$(info     Use docs/prompts/success-indicator-template.md to capture completion.)
$(if $(call is_aggregate,$(1)),$(if $(call aggregate_pending,$(1)),$(info   - Stream sub-tasks pending: $(call aggregate_pending,$(1)))$(info     Use `make stream.todo TASK=$(1)` or `make stream.next TASK=$(1)` to drill into this stream.),$(info   - Stream sub-tasks completed; attach the stream success indicator to finish.)))
$(info   - Suggested actions:)
$(info     1. Review dependencies and confirm prerequisites are satisfied.)
$(info     2. Implement the required updates and capture results/evidence.)
$(info     3. Update status via catalogue edit or make mark target and log outcomes.)
endef

define PRINT_NEXT_PROMPT
$(call PRINT_PROMPT_CORE,$(1))
$(info   - Sequence coverage: $(NEXT_INDEX) / $(pending_total))
$(info )
endef

define PRINT_PREV_PROMPT
$(info Completion recap:)
$(info   - Objective: $(TITLE_$(1)))
$(info   - Stream: $(call stream_label,$(STREAM_$(1))))
$(if $(call branch_name,$(1)),$(info   - Branch: $(call branch_name,$(1)) ($(call branch_label,$(1)))),$(info   - Branch: n/a))
$(info   - Current status: $(call effective_status,$(1)) ($(CLASS_$(1))))
$(info   - Dependencies: $(call task_dependencies,$(1)))
$(info   - Reference doc: $(REF_$(1)))
$(info   - Success indicator: $(call success_display,$(1)))
$(info     Search order: $(SUCCESS_SEARCH_DIRS))
$(info   - Follow-up ideas:)
$(info     1. Review artefacts or rerun validations if parity thresholds change.)
$(info     2. Update parity dashboards and logs if new evidence is captured.)
$(info   - Sequence coverage: $(PREV_INDEX) / $(completed_total))
$(info )
endef

define PRINT_STREAM
$(if $(call tasks_for_stream,$(1)),$(info )$(info -- $(call stream_label,$(1)) --)$(foreach t,$(call tasks_for_stream,$(1)),$(info $(call format_task,$(t)))))
endef

define PRINT_STREAM_FILTER
$(if $(call tasks_for_stream_status,$(1),$(2)),$(info )$(info -- $(call stream_label,$(1)) --)$(foreach t,$(call tasks_for_stream_status,$(1),$(2)),$(info $(call format_task,$(t)))))
endef

DEP_TARGETS :=

define REGISTER_DEP
$(eval DEPS_$(1) := $(strip $(DEPS_$(1)) $(2)))
$(eval DEP_TARGETS := $(sort $(DEP_TARGETS) $(1)))
endef

define CHECK_TASK_DEPS
$(foreach dep,$(DEPS_$(1)),$(if $(and $(filter done,$(STATUS_$(1))),$(filter-out done,$(STATUS_$(dep)))),\
$(warning $(1) marked done but prerequisite $(dep) is $(STATUS_$(dep))),))
endef

# --- Stream metadata -------------------------------------------------------------

include $(MAKEFILE_DIR)includes/meta.mk

# --- Task catalogue --------------------------------------------------------------

include $(MAKEFILE_DIR)includes/tasks.mk
